﻿using System;
using System.Collections.Generic;
using System.Text;
using log4net.Appender;
using log4net.Core;
using log4net.Util;
using Color = System.Drawing.Color;

using System.Threading;
using agsXMPP;
using agsXMPP.protocol.client;
using agsXMPP.protocol.extensions.html;
using agsXMPP.Xml.Dom;

namespace log4net.Extensions.Appender.XMPP
{
    public abstract class XMPPAppender:AppenderSkeleton
    {
        protected override bool RequiresLayout
        {
            get
            {
                return true;
            }
        }

        public XMPPAppender()
        {
            this.Port = 5222;
        }
        /// <summary>
        /// Host name of the XMPP server
        /// </summary>
        public string Server { get; set; }
        /// <summary>
        /// UserName to connect with. 
        /// </summary>
        public string UserName { get; set; }
        /// <summary>
        /// Password to connect with.
        /// </summary>
        public string Password { private get; set; }
        /// <summary>
        /// Port to connect to.
        /// </summary>
        public int Port { get; set; }

        public override void ActivateOptions()
        {
            _ClientConn = XMPPAppenderConnection.GetConnection(this.Server, this.Port, this.UserName, this.Password);
            _ClientConn.Conn.OnLogin += new ObjectHandler(_ClientConn_OnLogin);
            _ClientConn.Conn.OnError += new ErrorHandler(_ClientConn_OnError);
            _ClientConn.Conn.OnAuthError += new XmppElementHandler(_ClientConn_OnAuthError);
            //_ClientConn.Conn.Open(this.UserName, this.Password, Environment.MachineName);

            _MessageLoop = new Thread(new ThreadStart(messageLoopMain));
            _MessageLoop.IsBackground = true;
            _MessageLoop.Start();

            if (_ClientConn.Conn.XmppConnectionState == XmppConnectionState.Connected)
            {
                if (LogLog.IsDebugEnabled) LogLog.Debug("ActivateOptions() - Already connected to " + this.Server + " calling OnConnected");
                OnConnected(_ClientConn);
                _Connected.Set();
            }

            _LevelMapping.ActivateOptions();
        }

        protected virtual void OnConnected(XMPPAppenderConnection conn)
        {

        }

        void _ClientConn_OnAuthError(object sender, agsXMPP.Xml.Dom.Element e)
        {
            if (LogLog.IsErrorEnabled) LogLog.Error("_ClientConn_OnAuthError - " + e);
        }

        void _ClientConn_OnError(object sender, Exception ex)
        {
            if (LogLog.IsErrorEnabled) LogLog.Error("_ClientConn_OnError - Exception thrown", ex);
        }


        void _ClientConn_OnLogin(object sender)
        {
            if (LogLog.IsDebugEnabled) LogLog.Debug("_ClientConn_OnLogin - Connected to " + this.Server);
            _Connected.Set();

            OnConnected(_ClientConn);
        }

        /// <summary>
        /// Method is used to alter the background color of a message based on the 
        /// level of the event. 
        /// </summary>
        /// <param name="level">Level of the logging event.</param>
        /// <returns>if level is null, then Color.Empty will be returned. Otherwise a color will be returned.</returns>
        //protected Color GetBackgroundColor(Level level)
        //{
        //    Color color = Color.Empty;

        //    if (null == level)
        //    {
               
        //    }
        //    else if (level >= Level.Warn && level <= Level.Error)
        //    {
        //        color = Color.Yellow;
        //    }
        //    else if (level >= Level.Error)
        //    {
        //        color = Color.Red;
        //    }

        //    return color;
        //}

        private ManualResetEvent _Connected = new ManualResetEvent(false);
        private AutoResetEvent _MessageReady = new AutoResetEvent(false);
        private bool _Monitor = true;
        private Thread _MessageLoop;
        private XMPPAppenderConnection _ClientConn;
        private Queue<Message> _MessageQueue = new Queue<Message>();
        private object _MessageLock = new object();
        private LevelMapping _LevelMapping = new LevelMapping();

        public void AddMapping(LevelColors mapping)
        {
            _LevelMapping.Add(mapping);
        }

        private void messageLoopMain()
        {
            try
            {
                _Connected.WaitOne(); //TODO: Fix this could block forever
                
                //TODO: Setup the connection. 


                while (_Monitor)
                {
                    Message[] messages = null;

                    try
                    {
                        if (!_MessageReady.WaitOne(_MaximumWaitTimeout, false))
                        {
                            if (LogLog.IsDebugEnabled) LogLog.Debug("XMPPAppender.messageLoopMain() - Message wait interval exceeded, checking for unsent messages. Interval = " + _MaximumWaitTimeout);
                        }

                        lock (_MessageLock)
                        {
                            if (_MessageQueue.Count == 0)
                                continue;
                            messages = _MessageQueue.ToArray();
                            _MessageQueue.Clear();
                        }
                    }
                    catch (Exception ex0)
                    {
                        if (LogLog.IsErrorEnabled) LogLog.Error("XMPPAppender.messageLoopMain() - Exception while processing message", ex0);
                    }

                    if (messages == null || messages.Length == 0)
                        continue;

                    foreach (Message message in messages)
                    {
                        try
                        {
                            _ClientConn.Conn.Send(message);
                        }
                        catch (Exception ex0)
                        {
                            if (LogLog.IsErrorEnabled) LogLog.Error("XMPPAppender.messageLoopMain() - Exception while sending message", ex0);
                        }
                    }


                }
            }
            catch (Exception ex)
            {
                if (LogLog.IsErrorEnabled) LogLog.Error("XMPPAppender.messageLoopMain() - Exception thrown exiting", ex);
            }
        }

        private TimeSpan _MaximumWaitTimeout = new TimeSpan(0, 1, 0);
        private TimeSpan _CloseTimeout = new TimeSpan(0, 0, 30);


        protected override void OnClose()
        {
            _Monitor = false;
            if (LogLog.IsWarnEnabled && !_MessageLoop.Join(_CloseTimeout))
            {
                LogLog.Warn("XMPPAppender.OnClose() - Took longer than timeout interval to stop. CloseTimeout = " + _CloseTimeout);
            }
        }


        protected void QueueMessage(Message message)
        {
            if (null == message)
                return;

            lock (_MessageLock)
            {
                if (_MessageQueue.Count < 10000)
                {
                    _MessageQueue.Enqueue(message);
                    _MessageReady.Set();
                }
            }
        }

        protected Message GetMessage(LoggingEvent loggingEvent)
        {
            string renderedMessage = base.RenderLoggingEvent(loggingEvent).Trim();
            Message message = new Message();
            message.Type = MessageType.groupchat;

            object test = _LevelMapping.Lookup(loggingEvent.Level);
            LevelColors colors = _LevelMapping.Lookup(loggingEvent.Level) as LevelColors;

            if (null == colors)
            {
                message.Body = renderedMessage;
            }
            else
            {
                Html html = new Html();
                html.Body = new Body();
                Element element = new Element("span");
                element.Value = renderedMessage;
                element.Attributes.Add("style", colors.ToString());
                html.Body.AddChild(element);
                message.Html = html;
            }


            //TODO: Check and see if we can skip setting the body since we are sending a html message.
            //message.Body = renderedMessage
                       return message;
        }

        public class LevelColors : LevelMappingEntry
        {
            public LevelColors()
            {

            }
          

            public string ForeColor { get; set; }
            public string BackColor { get; set; }

            string _ValidatedForeColor="#000000"; 
            string _ValidatedBackColor="#FFFFFF";

            public override void ActivateOptions()
            {
                _ValidatedForeColor = GetHtmlColor(this.ForeColor, Color.Black);
                _ValidatedBackColor = GetHtmlColor(this.BackColor, Color.White);
            }

            public string GetHtmlColor(string value, Color defaultValue)
            {
                Color color = Color.Empty;

                try
                {
                    color = System.Drawing.ColorTranslator.FromHtml(value);
                }
                catch (Exception ex)
                {
                    if (LogLog.IsWarnEnabled) LogLog.Warn("LevelColors.GetHtmlColor - Exception parsing html color " + value, ex);
                    color = defaultValue;
                }

                return System.Drawing.ColorTranslator.ToHtml(color);
            }

            public override string ToString()
            {
                return string.Format("background:{0};color:{1}", _ValidatedBackColor, _ValidatedForeColor);
            }
        }
    }
}
